package com.dferreira.repository;

import android.content.Context;
import android.os.AsyncTask;

import com.dferreira.languagesteach.utils.DataPaths;
import com.dferreira.repository.data_delegators.DefaultDataSource;
import com.dferreira.repository.data_delegators.ExternalPackageQueries;
import com.dferreira.repository.data_delegators.RepositoryDataSource;
import com.dferreira.repository.data_models.PackageMetadata;
import com.dferreira.repository.data_models.PackageMetadataState;
import com.dferreira.repository.data_models.RepositoryPackage;
import com.dferreira.repository.services.PackageManagerService;

import java.util.Iterator;

public class Repository {

    private final Context activity;
    private final RepositoryDataSource remoteDataSource;
    private final DefaultDataSource mDefaultDataSource;
    private final ExternalPackageQueries externalPackageQueries;

    /* Database constants */
    // Create local package data
    private final DataPaths dataPaths;

    /**
     * Constructor
     *
     * @param context activity that is requiring the handle from the repository
     */
    public Repository(Context context) {
        this.activity = context;
        this.dataPaths = new DataPaths(context);
        this.remoteDataSource = new RepositoryDataSource(context);
        this.externalPackageQueries = new ExternalPackageQueries(context);
        this.mDefaultDataSource = new DefaultDataSource(context);
    }

    /**
     * Create the database structure
     */
    public void prepare() {

        if (this.remoteDataSource.prepare() == null) {
            this.mDefaultDataSource.install(false);
        }
    }

    /**
     * Will install the default packages that are required to initiate the app
     */
    public void forceDefaultPackageInstallation() {
        this.mDefaultDataSource.install(true);
    }

    /**
     * Updates the tables from the repository
     */
    public void updateRepositoryData() {
        if (dataPaths.isNetworkAvailable()) {
            if (remoteDataSource.getStatus() == AsyncTask.Status.PENDING) {
                remoteDataSource.execute();
            }
        }
    }

    /**
     * @return false   ->  Update repository it is yet running
     * true    ->  Update repository already finish
     */
    public boolean updateRepositoryAlreadyFinish() {
        return remoteDataSource.getStatus() == AsyncTask.Status.FINISHED;
    }

    /**
     * verifies if the package is already in the table from installedPackages
     * and if he and is dependencies are in a valid state if is the case will
     * return with do any modifications otherwise will install the package or
     * his dependencies
     *
     * @param packageName name from the package to install, for instance pt
     */
    public void installPackage(String packageName) {
        long totalBytesToInstall;

        totalBytesToInstall = getInstallBudget(packageName);
        PackageManagerService.installPackage(this.activity, totalBytesToInstall, packageName);
    }

    /**
     * Get the required space to install a package
     *
     * @param pkgName name from the package to get his budget
     * @return required space to install the package in bytes
     */
    public long getInstallBudget(String pkgName) {
        RepositoryPackage pkgInfo;
        Iterator<RepositoryPackage> dependenciesIterator;
        long sizesSum;
        RepositoryPackage dependency;

        if (isPackageInstalled(pkgName)) {
            return 0;
        } else {
            pkgInfo = remoteDataSource.getPackageInfo(pkgName);
            if (pkgInfo == null) {
                return -1;
            }
            sizesSum = pkgInfo.getUncompressedSize();
            if (pkgInfo.getDependencies() == null) {
                return sizesSum;
            }
            dependenciesIterator = pkgInfo.getDependencies().iterator();
            while (dependenciesIterator.hasNext()) {
                dependency = dependenciesIterator.next();
                if (!isPackageInstalled(dependency.getName())) {
                    sizesSum += getInstallBudget(dependency.getName());
                }
            }
            return sizesSum;
        }
    }

    /**
     * Uninstalls a damage package or some of his broken dependencies
     *
     * @param packageName Name from the package to uninstall
     */
    public void uninstallDamagedPkg(String packageName) {

        PackageManagerService.uninstallPkg(this.activity, packageName);
    }


    /**
     * @param packageName name from the package to search
     * @return Available info about the package to search null if package don't
     * exist
     */
    public PackageMetadata getInstalledPackageInfo(String packageName) {
        return this.externalPackageQueries.getPackageInfo(packageName);
    }


    /**
     * Verify if a package is already installed
     *
     * @param packageName package name
     * @return true -> package installed
     * false -> package no installed
     */
    public boolean isPackageInstalled(String packageName) {
        PackageMetadata packageMetadata = this.externalPackageQueries.getPackageInfo(packageName);
        return (packageMetadata != null) && (packageMetadata.getState().equals(PackageMetadataState.Installed));
    }

    /**
     * Verify if a package needs to be recovered
     *
     * @param packageName name from the package to perform this verification
     * @return false -> Package does not need any recovery
     * true -> The package needs to be recovered
     */
    public boolean packageNeedsToBeRecovered(String packageName) {
        PackageMetadata packageMetadata = this.externalPackageQueries.getPackageInfo(packageName);
        return (packageMetadata != null) && (!packageMetadata.getState().equals(PackageMetadataState.Installed.name()));
    }


    /**
     * Allows to monitor the installation progress from a package
     *
     * @return percentage from the information already installed
     */
    public Integer getInstallationProgress(String packageName) {
        return PackageManagerService.getPackageInstallationProgress(packageName);
    }

    /**
     * When this method is called mean the installation from a package must be cancelled
     */
    public void cancelInstallation(String packageName) {
        //TODO Should set the package state to cancel
        //packageManagerService.cancelInstallation(packageName);
    }

}
